unit uniHeroAmmo;

interface

uses
  uniAmmo,
  uniEnemyAircraft,
  uniDefine,
  SysUtils,
  gl,
  Classes;

type
  { HeroAmmo }

  HeroAmmo = class(TObject)
  public
    ammoRoot: array[0..NUM_HERO_AMMO_TYPES - 1] of ActiveAmmo;
    ammoPool: ActiveAmmo;
    ammoSize: array[0..NUM_HERO_AMMO_TYPES - 1, 0..1] of Single;
    ammoDamage: array[0..NUM_HERO_AMMO_TYPES - 1] of Single;

    ammoTex: array[0..NUM_HERO_AMMO_TYPES - 1] of GLuint;

    //game: Global;


    constructor Create;
    destructor Destroy; override;

    procedure addAmmo(_type: Integer; pos: array of Single);
    procedure updateAdmmo;

    procedure checkForHits(fleet: EnemyFleet);

    procedure printList(_type: Integer);

    procedure drawGL;
    procedure clear;

    procedure loadTextures;
    procedure deleteTextures;

    function getNewAmmo: ActiveAmmo;
    procedure killAmmo(dead: ActiveAmmo);


  end;


implementation

uses
  PNGLoader,
  uniExplosions,
  uniConfig,
  uniGlobal;

{ HeroAmmo }

procedure HeroAmmo.addAmmo(_type: Integer; pos: array of Single);
var
  vel: array[0..2] of Single;
  newAmmo, first: ActiveAmmo;
begin
  vel[0] := 0;
  vel[1] := 0;
  vel[2] := 0;


  case _type of
    0: vel[1] := 0.5 * uniGlobal.speedAdj;
    1: vel[1] := 0.2 * uniGlobal.speedAdj;
    2: vel[1] := 0.3 * uniGlobal.speedAdj;
  end;
  if (_type >= 0) and (_type < NUM_HERO_AMMO_TYPES) then
  begin
    //fprintf(stderr, "HeroAmmo::addAmmo(%d, <%f %f %f> )\n", type, pos[0], pos[1], pos[2]);
    Writeln('HeroAmmo::addAmmo(', _type, ',', pos[0], ',', pos[1], ',', pos[2], ')');
    newAmmo := getNewAmmo();
    newAmmo.init(pos, vel, ammoDamage[_type]);
    newAmmo.back := ammoRoot[_type];
    newAmmo.next := ammoRoot[_type].next;
    if Boolean(ammoRoot[_type].next) then
    begin
      first := ammoRoot[_type].next;
      first.back := newAmmo;
    end;
    ammoRoot[_type].next := newAmmo;
  end;
end;

procedure HeroAmmo.checkForHits(fleet: EnemyFleet);
var
  i: Integer;
  minShipY: Single;
  thisAmmo, backAmmo, nextAmmo: ActiveAmmo;
  enemy: EnemyAircraft;
begin

  minShipY := 100.0;
  //-- Get minimum ship Y location so we can ignore some of the ammunition
  fleet.toFirst;
  enemy := fleet.getShip;
  if not Boolean(enemy) then //-- no ships - return immediately
    Exit;
  while Boolean(enemy) do
  begin
    if (enemy.pos[1] - 3.0 < minShipY) then
      minShipY := enemy.pos[1] - 3.0;
    enemy := fleet.getShip;
  end;
  //-- Go through all the ammunition and check for hits
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    thisAmmo := ammoRoot[i].next;
    while Boolean(thisAmmo) do
    begin
      if thisAmmo.pos[1] < minShipY then
      begin
        thisAmmo := thisAmmo.next;
        continue;
      end;
      fleet.toFirst;
      enemy := fleet.getShip;
      while Boolean(enemy) do
      begin
        if enemy.checkHit(thisAmmo) then
        begin
          //do damage
          if i = 1 then
            enemy.damage := enemy.damage + ammoDamage[i] * uniGlobal.speedAdj
          else
            enemy.damage := enemy.damage + ammoDamage[i];
          //add explosion
          _explosions.addExplo(ExploType(Integer(HeroAmmo00) + i), thisAmmo.pos);
          if i <> 1 then // ammo type 1 doesn't get killed
          begin
            backAmmo := thisAmmo.back;
            nextAmmo := thisAmmo.next;
            backAmmo.next := nextAmmo;
            if Boolean(nextAmmo) then nextAmmo.back := backAmmo;
            killAmmo(thisAmmo);
            thisAmmo := backAmmo;
            enemy := nil; //-- break out of enemy loop
          end
          else
            enemy := fleet.getShip;
        end
        else
          enemy := fleet.getShip;
      end;
      thisAmmo := thisAmmo.next;
    end;
  end;
end;

procedure HeroAmmo.clear;
var
  i: Integer;
  cur, del: ActiveAmmo;
begin
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    cur := ammoRoot[i].next;
    while Boolean(cur) do
    begin
      del := cur;
      cur := cur.next;
      killAmmo(del);
    end;
    ammoRoot[i].next := nil;
  end;
end;

constructor HeroAmmo.Create;
var
  i: Integer;
begin
  //game = Global::getInstance();

  //-- initialize everything to sane values...
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    ammoRoot[i] := ActiveAmmo.Create;
    ammoSize[i][0] := 0.1;
    ammoSize[i][0] := 0.5;
    ammoDamage[i] := 2.5;
  end;
  ammoPool := ActiveAmmo.Create;
  loadTextures;
  ammoSize[0][0] := 0.05; ammoSize[0][1] := 0.65;
  ammoSize[1][0] := 0.11; ammoSize[1][1] := 1.5;
  ammoSize[2][0] := 0.3; ammoSize[2][1] := 1.5;
  ammoDamage[0] := 3.5;
  ammoDamage[1] := 6.0;
  ammoDamage[2] := 40.0;
end;

procedure HeroAmmo.deleteTextures;
var
  i: Integer;
begin
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    glDeleteTextures(1, @ammoTex[i]);
    ammoTex[i] := 0;
  end;
end;

destructor HeroAmmo.Destroy;
var
  cur, del: ActiveAmmo;
  i: Integer;
begin
  clear;
  cur := ammoPool.next;
  while Boolean(cur) do
  begin
    del := cur;
    cur := cur.next;
    del.Destroy;
  end;

  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    ammoRoot[i].Destroy;
  end;
  ammoPool.Destroy;
  deleteTextures;
  inherited;
end;

procedure HeroAmmo.drawGL;
var
  i: Integer;
  pos: array of Single;
  thisAmmo: ActiveAmmo;
begin
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    glColor4f(1.0, 1.0, 1.0, 1.0);
    glBindTexture(GL_TEXTURE_2D, ammoTex[i]);
    thisAmmo := ammoRoot[i].next;
    glBegin(GL_QUADS);
    while Boolean(thisAmmo) do
    begin
      pos := @thisAmmo.pos;
      glTexCoord2f(0.0, 0.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1], pos[2]);
      glTexCoord2f(0.0, 1.0); glVertex3f(pos[0] - ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
      glTexCoord2f(1.0, 1.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1] - ammoSize[i][1], pos[2]);
      glTexCoord2f(1.0, 0.0); glVertex3f(pos[0] + ammoSize[i][0], pos[1], pos[2]);
      thisAmmo := thisAmmo.next; //ADVANCE
    end;
    glEnd;
  end;
end;

function HeroAmmo.getNewAmmo: ActiveAmmo;
var
  retAmmo: ActiveAmmo;
begin
  if Boolean(ammoPool.next) then
  begin
    retAmmo := ammoPool.next;
    ammoPool.next := retAmmo.next;
  end
  else
  begin
    retAmmo := ActiveAmmo.Create;
  end;

  Result := retAmmo;
end;

procedure HeroAmmo.killAmmo(dead: ActiveAmmo);
begin
  dead.back := nil;
  dead.next := ammoPool.next;
  ammoPool.next := dead;
end;

procedure HeroAmmo.loadTextures;
var
  i : Integer;
  filename : string;
begin
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    filename := 'png/heroAmmo' +  FormatCurr('0#', i) + '.png';
    LoadTexture(filename, ammoTex[i]);
  end;

end;

procedure HeroAmmo.printList(_type: Integer);
var
  thisAmmo: ActiveAmmo;
begin
  if (_type >= 0) and (_type < NUM_HERO_AMMO_TYPES) then
  begin
    thisAmmo := ammoRoot[_type];
    while Boolean(thisAmmo) do
    begin
      Writeln(thisAmmo.back.ClassName, '<--', thisAmmo.ClassName, '-->',thisAmmo.next.ClassName);
      thisAmmo := thisAmmo.next;
    end;
  end;
end;

procedure HeroAmmo.updateAdmmo;
var
  _config : Config;
  i: Integer;
  thisAmmo: ActiveAmmo;
  backAmmo, nextAmmo: ActiveAmmo;
begin
  for i := 0 to NUM_HERO_AMMO_TYPES - 1 do
  begin
    thisAmmo := ammoRoot[i].next;
    while boolean(thisAmmo) do
    begin
      //-- clean up ammo
      if thisAmmo.pos[1] > _config.screenBoundX then // remove ammo
      begin
        backAmmo := thisAmmo.back;
        nextAmmo := thisAmmo.next;
        backAmmo.next := nextAmmo;
        if Boolean(nextAmmo) then
        begin
          nextAmmo.back := backAmmo;
        end;
        killAmmo(thisAmmo);
        thisAmmo := nextAmmo; // Advance
      end
      else
      begin
        thisAmmo.updatePos;
        thisAmmo := thisAmmo.next; // Advance
      end;
    end;
  end;
end;

end.

